package com.zzy.furns.web;

import com.google.gson.Gson;
import com.zzy.furns.domain.Cart;
import com.zzy.furns.domain.CartItem;
import com.zzy.furns.domain.Furn;
import com.zzy.furns.service.FurnService;
import com.zzy.furns.service.impl.FurnServiceImpl;
import com.zzy.furns.utils.DataUtils;
import org.apache.commons.beanutils.BeanUtils;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;

public class CartServlet extends BasicServlet{

    private FurnService furnService = new FurnServiceImpl();
    private static final Integer cartItemCount = 1;
    private final Object OBJECT_STOCK = new Object();

    //添加家居到购物车
    protected void addItem(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException, InvocationTargetException, IllegalAccessException {

        //获取添加家居的id
        Integer id = DataUtils.parseInt(req.getParameter("id"));
        //根据id查询家居信息
        //个人感悟：尽量保证参数正确时在访问数据库，既保护数据又减缓数据库压力
        if (id == -1){
            System.out.println("家居id错误");
            resp.sendRedirect(req.getHeader("Referer"));
            return;
        }

        //考虑到多线程问题，做简单处理（会造成线程阻塞 但目前只会对象互斥锁）
        synchronized (OBJECT_STOCK) {
            //此处应该考虑线程安全问题 场景：当多个线程同时到达，但是Furn库存数只有1时会出现超卖现象
            Furn furn = furnService.queryFurnById(id);

            if (furn == null || furn.getStock() <= 0) {
                req.getSession().setAttribute("msg","该家居已售罄");
                System.out.println("该家居已售罄");
                resp.sendRedirect(req.getHeader("Referer"));
                return;
            }

            //更新数据库中Furn库存数量 使用一个新的Furn对象完成更新操作 不破坏Furn furn = furnService.queryFurnById(id)对象数据
            Furn updateFurn = new Furn();
            BeanUtils.copyProperties(updateFurn, furn);
            //如果多个线程同时执行，会出现商品库存数量-1，但是购物车中该家居的数量有多个
            updateFurn.setStock(updateFurn.getStock() - 1);
            if (!furnService.updateFurn(updateFurn)) {
                System.out.println("库存更新错误");
                resp.sendRedirect(req.getHeader("Referer"));
                return;
            }

            //根据家居信息创建CartItem
            CartItem cartItem = new CartItem(furn.getId(), furn.getName(), cartItemCount, furn.getPrice(), furn.getPrice());

            /**
             * 每一个用户对应一个购物车 ---> Session
             *      不能每次都new一个Cart，这样会导致Cart中用永远只有新增的这条CartItem
             */
            //如果多个线程同时执行，会出现只保留一件家居信息的情况
            Cart oldCart = (Cart) req.getSession().getAttribute("cart");

            /**
             * 使用对象互斥锁也存在Cart中count=1的情况猜想
             *      多个请求线程携带的JSESSIONID不一致 导致每次都创建了新的Session
             */

            try {
                if (oldCart == null) {
                    //如果cart为空，说明Session中没有存储Cart
                    Cart cart = new Cart();
                    cart.addItem(cartItem);
                    req.getSession().setAttribute("cart", cart);
                } else {
                    //如果cart不为空，说明Session中已经存储了Cart
                    oldCart.addItem(cartItem);
                }
            } catch (Exception e) {
                //发生异常，回滚数据
                if (!furnService.updateFurn(furn)) {
                    System.out.println("数据回滚异常，请手动处理。原数据= " + furn);
                } else {
                    System.out.println("数据回滚成功");
                }
            }
        }

        System.out.println(req.getSession().getAttribute("cart"));

//        Integer totalCount = ((Cart) req.getSession().getAttribute("cart")).getTotalCount();
//        req.getSession().setAttribute("totalCount",totalCount);

        /**
         * resp.sendRedirect(req.getContextPath()); 这样跳转的问题
         * 如果是在第二页添加家居，则添加完成后会跳转到第一页
         * 我们希望是在哪页添加家居后依然跳转回哪页
         */
//        resp.sendRedirect(req.getHeader("Referer"));
        Map<String, Integer> map = new HashMap<>();
        Cart cart = (Cart) req.getSession().getAttribute("cart");
        map.put("totalCount",cart.getTotalCount());
        DataUtils.sendJSON(map,resp);
    }

    /**
     * 修改购物车商品数量
     */
    protected void updateCount(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException, InvocationTargetException, IllegalAccessException {
        Integer id = DataUtils.parseInt(req.getParameter("id"));
        Integer newCount = DataUtils.parseInt(req.getParameter("newCount"));

        //根据id查询对应的furn信息
        Furn furn = furnService.queryFurnById(id);
        //获取库存信息，这里需要再内存中保留这个furn对象，以防后续用来恢复数据库数据时使用
        Long oldStock = furn.getStock();

        //获取该用户的购物车
        Cart cart = (Cart) req.getSession().getAttribute("cart");

        /**
         * 进行对象属性拷贝，保存原始数据，用于后续发生异常时恢复原始购物车数据
         * （终于明白了为什么源码中好多方法首先是对数据数据进行拷贝，而不是直接操作输入数据了）
         */
        Cart copyCart = new Cart();
        BeanUtils.copyProperties(copyCart,cart);

        //调用修改购物车商品数量的对应方法
        Long updateStock;
        try {
            updateStock = cart.updateCount(id, newCount, oldStock);
        } catch (Exception e) {
            throw new RuntimeException("修改购物车商品数量时发生异常，异常信息= " + e.getMessage());
        }

        if (updateStock != oldStock){
            //库存信息发生变动，更新库存信息
            furn.setStock(updateStock);
            if (furnService.updateFurn(furn)){
                System.out.println("库存信息更新成功");

//                resp.sendRedirect(req.getContextPath() + "/views/cart/cart.jsp");
                //购物车界面可能分页显示，尽量避免硬编码
                //返回原页面
                resp.sendRedirect(req.getHeader("Referer"));
            } else {
                System.out.println("库存信息更新失败");
                //恢复购物车原始数据（状态），保证数据的一致性
                req.getSession().setAttribute("cart",copyCart);
                System.out.println("购物车数据恢复成功");
                //返回原页面
                resp.sendRedirect(req.getHeader("Referer"));
            }
        }
    }

    /**
     * 删除购物车
     */
    protected void deleteItem(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException, InvocationTargetException, IllegalAccessException {
        Integer id = DataUtils.parseInt(req.getParameter("id"));

        //根据id查询家居信息，用于修改库存数量
        Furn furn = furnService.queryFurnById(id);

        if (furn == null){
            //没有对应的家居 防止绕过前端，直接请求后端接口
            System.out.println("数据库中不存在id= " + id + " 的家居信息");
            return;
        }

        //获取当前用户的购物车对象
        Cart cart = (Cart) req.getSession().getAttribute("cart");
        //备份购物车数据
        Cart copyCart = new Cart();
        BeanUtils.copyProperties(copyCart,cart);

        //调用Cart的deleteItem方法，返回值为删除的CartItem对象
        CartItem cartItem = cart.deleteItem(id);

        if (cartItem == null){
            //购物车中不存在要删除的家居数据，通常由绕过前端，直接请求后端接口引起 (可以通过Apifox测试)
            System.out.println("购物车中不存在id= " + id + " 的家居信息");
        } else {
            //购物车删除成功，更新数据库中的库存数据
            furn.setStock(furn.getStock() + cartItem.getCount());
            if (furnService.updateFurn(furn)){
                //库存数据更新成功 跳转回原页面
                resp.sendRedirect(req.getHeader("Referer"));
            } else {
                //库存数据更新失败，恢复购物车数据 跳转回原页面
                req.getSession().setAttribute("cart",copyCart);
                resp.sendRedirect(req.getHeader("Referer"));
            }
        }
    }

    /**
     * 清空购物车
     */
    protected void clear(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException, InvocationTargetException, IllegalAccessException {
        //获取当前用户的购物车
        Cart cart = (Cart) req.getSession().getAttribute("cart");
        //备份购物车数据，用于库存数据更新失败时恢复购物车数据
        Cart copyCart = new Cart();
        BeanUtils.copyProperties(copyCart,cart);

        Map<Integer, Integer> map = cart.clear();

        //根据id与对应的count修改库存数据
        for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
            //根据id查询家居信息
            Furn furn = furnService.queryFurnById(entry.getKey());
            //根据count修改库存数据
            furn.setStock(furn.getStock() + entry.getValue());
            if (!furnService.updateFurn(furn)){
                //库存更新失败，回滚购物车信息，跳出循环
                System.out.println("家居【 " + furn.getName() + " 】库存数据修改失败，回滚购物车数据...");
                req.getSession().setAttribute("cart",copyCart);
                break;
            }
        }

        //重定向回原页面
        resp.sendRedirect(req.getHeader("Referer"));
    }
}
